前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 集群 cluster

30 秒速记

  • Node.js 单进程只用一个核 → cluster 起多个 worker 进程,共享同一个端口
  • primary(老版本叫 master)负责派生、监控、重启,worker 负责处理请求
  • 每个 worker 内存、事件循环都独立 → session、缓存、定时任务不能放进程内存里
  • worker 崩了要自动补,但要加退避,避免启动即崩的无限重启
  • 现在很多团队直接用 PM2 或容器副本 + 负载均衡,原理一样

cluster 解决的是「Node.js 一个进程只能用一个 CPU 核」的问题:主进程派生出多个 worker,它们共享同一个端口一起接请求。 每个 worker 都是独立进程,内存和事件循环互不相通,所以我做集群时第一件事就是把状态挪出去,session 放 Redis,定时任务只让一个进程跑,不然请求落到不同 worker 上就会出现「刚登录又掉了」。主进程监听 exit 事件补新进程,但要加退避,否则代码一启动就崩时会疯狂重启把机器拖垮。线上我更常用 PM2 的 cluster 模式,底层就是这个模块。

理解「集群 cluster」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 process 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

一个最小可跑的 cluster 例子,能直观看到「多个进程共享一个端口」:

const cluster = require('cluster');
const http = require('http');
const os = require('os');

if (cluster.isPrimary) { // Node 16 之前叫 cluster.isMaster
  const n = os.availableParallelism ? os.availableParallelism() : os.cpus().length;
  for (let i = 0; i < n; i++) cluster.fork();
  cluster.on('exit', (w, code) => {
    console.log(`worker ${w.process.pid} 退出,code=${code},补一个`);
    cluster.fork(); // 生产里要加退避
  });
} else {
  http.createServer((req, res) => {
    res.end(`handled by ${process.pid}\n`);
  }).listen(3000);
}
@前端进阶之旅: 代码已经复制到剪贴板

连续 curl localhost:3000 几次,会看到返回的 pid 在变,这说明请求被分到了不同的 worker。用 kill <pid> 杀掉其中一个,主进程会收到 exit 事件并补一个新的。

几个要记住的事实:worker 是通过 child_process.fork 创建的,和主进程之间有一条 IPC 通道,可以 process.send 互发消息;默认调度策略在除 Windows 外的平台上是主进程轮询分发(SCHED_RR),可以通过 cluster.schedulingPolicy 或环境变量 NODE_CLUSTER_SCHED_POLICY 修改;每个 worker 都是完整的 V8 实例,内存各占一份,进程数一般不要超过 CPU 核数。

面试官追问

追问 1监控上看到 4 个 Node.js 进程,就能说集群可用了吗?
参考回答

不能。进程活着不代表在接流量,要压一下看请求是否分摊到各个 pid,再杀掉一个 worker,看它会不会被补上、期间请求有没有大面积失败。

追问 2用户反馈刚登录完,刷新一下又要重新登录,服务刚改成 cluster,怎么回事?
参考回答

session 存在进程内存里,第二次请求落到了另一个 worker。把 session 挪到 Redis,或者改用 JWT 这类无状态方案。

追问 3代码一启动就抛错,主进程不停 fork,CPU 被打满,怎么防?
参考回答

exit 事件里别无脑立刻重启,要按次数做退避,比如 1s、2s、4s 递增,短时间内崩太多次就停下来并告警。

追问 4测试跑完了进程却不退出,怎么查?
参考回答

看是不是 worker 没被 disconnect 或 kill,还有没有没关的 server、定时器。用 cluster.workers 检查残留,测试收尾时主动关掉,而不是让 CI 超时杀掉。

# cluster模块概览

30 秒速记

  • 单个 Node.js 进程只跑在一个核上 → 多核机器要起多个实例才吃得满
  • 两种组织方式:多实例多端口 + 反向代理分发;或一个主进程接连接再分给子进程
  • Node.js 的 cluster 是第二种:对外一个端口,主进程把连接交给 worker
  • 业务代码几乎不用改,但进程内状态要外置(session、缓存、定时任务)

Node.js 一个进程只用一个 CPU 核,cluster 就是让多个进程一起接请求,把多核吃满,业务代码基本不用改。 多实例的组织方式常见有两种:一种是每个实例各占一个端口,前面用 Nginx 分发;另一种是一个主进程对外监听,拿到连接后交给子进程处理,Node.js 自带的 cluster 走的是第二种。说是「几乎不用改代码」,但前提是你的代码没有把状态放在进程内存里,这一点在改造前一定要先排查。

node实例是单线程作业的。在服务端编程中,通常会创建多个node实例来处理客户端的请求,以此提升系统的吞吐率。对这样多个node实例,我们称之为cluster(集群)。

借助node的cluster模块,开发者可以在几乎不修改原有项目代码的前提下,获得集群服务带来的好处。

集群有以下两种常见的实现方案,而node自带的cluster模块,采用了方案二。

面试官追问

追问 1产品说「Node.js 单线程,多写点异步代码就等于集群了」,怎么纠正?
参考回答

异步解决的是 I/O 等待时别闲着,但 JS 计算还是只在一个核上跑。CPU 吃满了要靠多进程,这才是 cluster 干的事。

追问 2改成 cluster 后吞吐几乎没涨,第一反应要不要加进程数?
参考回答

先别加。确认 worker 数是不是和核数匹配、请求是否真的分散到各个 pid;再看瓶颈是不是根本不在 CPU,比如数据库或下游接口慢,加进程只会让下游更挤。

追问 3cluster 里主进程是逐字节转发请求数据给 worker 吗?
参考回答

不是。主进程 accept 到连接后,把这个连接的句柄通过 IPC 传给 worker,之后数据直接在 worker 和客户端之间走,主进程不参与读写。

追问 4内存里有个计数器统计在线人数,改成 cluster 后数字对不上了,为什么?
参考回答

每个 worker 各有一份计数器,各算各的。挪到 Redis 用 INCR,或者让 worker 通过 IPC 汇报给主进程统一算。

追问 5cluster 和 PM2、多容器副本,怎么选?
参考回答

单机多核、想少折腾,用 PM2 -i max,它就是封装了 cluster。已经上 K8s 了就跑多个单进程副本,由平台负载均衡和重启,比在容器里再套 cluster 更好管。

# 方案一:多个node实例+多个端口

30 秒速记

  • 思路:起多个独立 Node.js 进程,各占一个端口(3001~3004),前面挂 Nginx 做反向代理分发
  • 优点:简单,进程之间完全隔离,挂一个不影响别的
  • 缺点:端口要管理,进程之间要交换数据只能靠外部(Redis、消息队列)
  • 浏览器只认域名和一个端口,「分发」这件事一定落在代理层,健康检查也放在那
  • 实际生产里 PM2 / Docker 多副本 + Nginx upstream 基本就是这个模式

这个方案就是开几个互不相干的 Node.js 进程,各自监听一个端口,前面用 Nginx 这类反向代理把请求分过去。 好处是简单粗暴,每个进程都是普通的单进程服务,挂一个其他照常跑,代理会把它摘掉。代价是端口多了要管,进程之间没有直接的通信通道,要共享会话、缓存只能放到 Redis 这类外部存储。说白了这就是现在最常见的部署形态,容器化之后每个容器一个进程,upstream 里配多个地址,跟这个思路是一回事。

集群内的node实例,各自监听不同的端口,再由反向代理实现请求到多个端口的分发。

  • 优点:实现简单,各实例相对独立,这对服务稳定性有好处。
  • 缺点:增加端口占用,进程之间通信比较麻烦。

面试官追问

追问 1起了 4 个实例分别监听 3001 到 3004,浏览器会自动轮着请求吗?
参考回答

不会。浏览器只访问 URL 里那个端口,根本不知道后面有几个实例。得有 Nginx 监听 80 / 443,在 upstream 里把 4 个地址配上,由它来轮询。

追问 2Nginx 那段配置大概怎么写?
参考回答

upstream api { server 127.0.0.1:3001; server 127.0.0.1:3002; },再在 location 里 proxy_pass http://api;。默认就是轮询,想按负载分可以加 least_conn,同一用户要落到同一台就用 ip_hash。

追问 33003
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
      • cluster模块概览
        • 方案一:多个node实例+多个端口
        • 方案二:主进程向子进程转发请求
      • 入门实例
      • cluster模块实现原理
      • 问题1:master、worker如何通信
      • 问题2:如何实现端口共享
      • master、worker内部通信小技巧
      • 相关链接
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

集群 cluster|Nodejs专题系列

# 集群 cluster

30 秒速记

  • Node.js 单进程只用一个核 → cluster 起多个 worker 进程,共享同一个端口
  • primary(老版本叫 master)负责派生、监控、重启,worker 负责处理请求
  • 每个 worker 内存、事件循环都独立 → session、缓存、定时任务不能放进程内存里
  • worker 崩了要自动补,但要加退避,避免启动即崩的无限重启
  • 现在很多团队直接用 PM2 或容器副本 + 负载均衡,原理一样

cluster 解决的是「Node.js 一个进程只能用一个 CPU 核」的问题:主进程派生出多个 worker,它们共享同一个端口一起接请求。 每个 worker 都是独立进程,内存和事件循环互不相通,所以我做集群时第一件事就是把状态挪出去,session 放 Redis,定时任务只让一个进程跑,不然请求落到不同 worker 上就会出现「刚登录又掉了」。主进程监听 exit 事件补新进程,但要加退避,否则代码一启动就崩时会疯狂重启把机器拖垮。线上我更常用 PM2 的 cluster 模式,底层就是这个模块。

理解「集群 cluster」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 process 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

一个最小可跑的 cluster 例子,能直观看到「多个进程共享一个端口」:

const cluster = require('cluster');
const http = require('http');
const os = require('os');

if (cluster.isPrimary) { // Node 16 之前叫 cluster.isMaster
  const n = os.availableParallelism ? os.availableParallelism() : os.cpus().length;
  for (let i = 0; i < n; i++) cluster.fork();
  cluster.on('exit', (w, code) => {
    console.log(`worker ${w.process.pid} 退出,code=${code},补一个`);
    cluster.fork(); // 生产里要加退避
  });
} else {
  http.createServer((req, res) => {
    res.end(`handled by ${process.pid}\n`);
  }).listen(3000);
}
@前端进阶之旅: 代码已经复制到剪贴板

连续 curl localhost:3000 几次,会看到返回的 pid 在变,这说明请求被分到了不同的 worker。用 kill <pid> 杀掉其中一个,主进程会收到 exit 事件并补一个新的。

几个要记住的事实:worker 是通过 child_process.fork 创建的,和主进程之间有一条 IPC 通道,可以 process.send 互发消息;默认调度策略在除 Windows 外的平台上是主进程轮询分发(SCHED_RR),可以通过 cluster.schedulingPolicy 或环境变量 NODE_CLUSTER_SCHED_POLICY 修改;每个 worker 都是完整的 V8 实例,内存各占一份,进程数一般不要超过 CPU 核数。

面试官追问

追问 1监控上看到 4 个 Node.js 进程,就能说集群可用了吗?
参考回答

不能。进程活着不代表在接流量,要压一下看请求是否分摊到各个 pid,再杀掉一个 worker,看它会不会被补上、期间请求有没有大面积失败。

追问 2用户反馈刚登录完,刷新一下又要重新登录,服务刚改成 cluster,怎么回事?
参考回答

session 存在进程内存里,第二次请求落到了另一个 worker。把 session 挪到 Redis,或者改用 JWT 这类无状态方案。

追问 3代码一启动就抛错,主进程不停 fork,CPU 被打满,怎么防?
参考回答

exit 事件里别无脑立刻重启,要按次数做退避,比如 1s、2s、4s 递增,短时间内崩太多次就停下来并告警。

追问 4测试跑完了进程却不退出,怎么查?
参考回答

看是不是 worker 没被 disconnect 或 kill,还有没有没关的 server、定时器。用 cluster.workers 检查残留,测试收尾时主动关掉,而不是让 CI 超时杀掉。

# cluster模块概览

30 秒速记

  • 单个 Node.js 进程只跑在一个核上 → 多核机器要起多个实例才吃得满
  • 两种组织方式:多实例多端口 + 反向代理分发;或一个主进程接连接再分给子进程
  • Node.js 的 cluster 是第二种:对外一个端口,主进程把连接交给 worker
  • 业务代码几乎不用改,但进程内状态要外置(session、缓存、定时任务)

Node.js 一个进程只用一个 CPU 核,cluster 就是让多个进程一起接请求,把多核吃满,业务代码基本不用改。 多实例的组织方式常见有两种:一种是每个实例各占一个端口,前面用 Nginx 分发;另一种是一个主进程对外监听,拿到连接后交给子进程处理,Node.js 自带的 cluster 走的是第二种。说是「几乎不用改代码」,但前提是你的代码没有把状态放在进程内存里,这一点在改造前一定要先排查。

node实例是单线程作业的。在服务端编程中,通常会创建多个node实例来处理客户端的请求,以此提升系统的吞吐率。对这样多个node实例,我们称之为cluster(集群)。

借助node的cluster模块,开发者可以在几乎不修改原有项目代码的前提下,获得集群服务带来的好处。

集群有以下两种常见的实现方案,而node自带的cluster模块,采用了方案二。

面试官追问

追问 1产品说「Node.js 单线程,多写点异步代码就等于集群了」,怎么纠正?
参考回答

异步解决的是 I/O 等待时别闲着,但 JS 计算还是只在一个核上跑。CPU 吃满了要靠多进程,这才是 cluster 干的事。

追问 2改成 cluster 后吞吐几乎没涨,第一反应要不要加进程数?
参考回答

先别加。确认 worker 数是不是和核数匹配、请求是否真的分散到各个 pid;再看瓶颈是不是根本不在 CPU,比如数据库或下游接口慢,加进程只会让下游更挤。

追问 3cluster 里主进程是逐字节转发请求数据给 worker 吗?
参考回答

不是。主进程 accept 到连接后,把这个连接的句柄通过 IPC 传给 worker,之后数据直接在 worker 和客户端之间走,主进程不参与读写。

追问 4内存里有个计数器统计在线人数,改成 cluster 后数字对不上了,为什么?
参考回答

每个 worker 各有一份计数器,各算各的。挪到 Redis 用 INCR,或者让 worker 通过 IPC 汇报给主进程统一算。

追问 5cluster 和 PM2、多容器副本,怎么选?
参考回答

单机多核、想少折腾,用 PM2 -i max,它就是封装了 cluster。已经上 K8s 了就跑多个单进程副本,由平台负载均衡和重启,比在容器里再套 cluster 更好管。

# 方案一:多个node实例+多个端口

30 秒速记

  • 思路:起多个独立 Node.js 进程,各占一个端口(3001~3004),前面挂 Nginx 做反向代理分发
  • 优点:简单,进程之间完全隔离,挂一个不影响别的
  • 缺点:端口要管理,进程之间要交换数据只能靠外部(Redis、消息队列)
  • 浏览器只认域名和一个端口,「分发」这件事一定落在代理层,健康检查也放在那
  • 实际生产里 PM2 / Docker 多副本 + Nginx upstream 基本就是这个模式

这个方案就是开几个互不相干的 Node.js 进程,各自监听一个端口,前面用 Nginx 这类反向代理把请求分过去。 好处是简单粗暴,每个进程都是普通的单进程服务,挂一个其他照常跑,代理会把它摘掉。代价是端口多了要管,进程之间没有直接的通信通道,要共享会话、缓存只能放到 Redis 这类外部存储。说白了这就是现在最常见的部署形态,容器化之后每个容器一个进程,upstream 里配多个地址,跟这个思路是一回事。

集群内的node实例,各自监听不同的端口,再由反向代理实现请求到多个端口的分发。

  • 优点:实现简单,各实例相对独立,这对服务稳定性有好处。
  • 缺点:增加端口占用,进程之间通信比较麻烦。

面试官追问

追问 1起了 4 个实例分别监听 3001 到 3004,浏览器会自动轮着请求吗?
参考回答

不会。浏览器只访问 URL 里那个端口,根本不知道后面有几个实例。得有 Nginx 监听 80 / 443,在 upstream 里把 4 个地址配上,由它来轮询。

追问 2Nginx 那段配置大概怎么写?
参考回答

upstream api { server 127.0.0.1:3001; server 127.0.0.1:3002; },再在 location 里 proxy_pass http://api;。默认就是轮询,想按负载分可以加 least_conn,同一用户要落到同一台就用 ip_hash。

追问 33003 那个进程一直没有访问日志,其他都正常,先看哪?
参考回答

先在本机 curl 127.0.0.1:3003 看它自己能不能响应,能响应就去查 Nginx 的 upstream 有没有写漏、改完有没有 nginx -s reload,再看 error.log 里有没有被标成不可用。进程活着不等于能接请求,这点要分开验证。

追问 4用户登录完刷新一下又掉线了,跟多实例有关吗?
参考回答

八成有关。会话如果存在进程内存里,下一次请求被分到别的实例就查不到了。正确做法是把会话放 Redis,ip_hash 这种粘性路由只能临时救急,实例一重启照样丢。

追问 5这个方案和 cluster 模块单端口方案,你一般选哪个?
参考回答

线上我更倾向多实例加代理,配合 PM2 或容器编排,扩到多台机器也是同一套。cluster 适合单机、不想额外配代理的场景,它解决的只是一台机器上用满多核。

# 方案二:主进程向子进程转发请求

30 秒速记

  • 思路:一个 master 对外监听唯一端口,按策略把连接分给多个 worker
  • 优点:只占一个端口,master 和 worker 之间有现成的 IPC,分发策略可控
  • 缺点:实现复杂,所有流量都过 master,它要足够稳
  • Node.js 的 cluster 模块默认就是这种模式(SCHED_RR)
  • 分的是 TCP 连接,不是单个 HTTP 请求;master 只负责派活和监督,不跑业务代码

这个方案是一个 master 进程对外占住唯一端口,收到连接后按策略转交给某个 worker 处理。 好处是只要一个端口,master 和 worker 是父子进程,天然有 IPC 通道,通信和分发策略都好控制。代价是入口集中在 master,它要是挂了整个服务就没了,所以 master 里一般只放派生、监听退出、重新拉起这些轻量逻辑,业务代码一行都不放。Node.js 自带的 cluster 模块默认走的就是这条路。

集群内,创建一个主进程(master),以及若干个子进程(worker)。由master监听客户端连接请求,并根据特定的策略,转发给worker。

  • 优点:通常只占用一个端口,通信相对简单,转发策略更灵活。
  • 缺点:实现相对复杂,对主进程的稳定性要求较高。

← 9.4 资源压缩 zlib9.6 v8 →

fe